using System;
using System.Collections.Generic;
using System.Linq;
using Unbound.Configuration;
using Unbound.Unbinders;
using Unbound.ValueUnbinders;

namespace Unbound
{
	public class Unbinder : IUnbinder
	{
		readonly ComplexUnbinder _complex;
		readonly EnumerableUnbinder _enumerable;
		readonly SimpleUnbinder _simple;
		readonly IValueUnbinder _valueUnbinder;
		readonly NullUnbinder _null;

		public Unbinder(ITypeDescriptor typeDescriptor,
		                ITypeSimplicityEvaluator typeSimplicityEvaluator,
		                IValueUnbinder valueUnbinder)
		{
			_valueUnbinder = valueUnbinder;
			_null = new NullUnbinder();
			_simple = new SimpleUnbinder(_valueUnbinder, typeSimplicityEvaluator);
			_enumerable = new EnumerableUnbinder(this);
			_complex = new ComplexUnbinder(typeDescriptor, this);
		}

		public Unbinder() : this(new CachedTypeDescriptor(),
		                         new CachedTypeSimplicityEvaluator(),
		                         new ValueUnbinder())
		{
		}

		public IDictionary<string, object> Unbind(object request, string prefix)
		{
			return Unbind(options =>
			              	{
			              		options.Prefix(prefix);
			              		options.Source(request);
			              	});
		}

		public IDictionary<string, object> Unbind(object request)
		{
			return Unbind(options => options.Source(request));
		}

		public IDictionary<string, object> Unbind(Action<IUnbindingOptions> options)
		{
			var unbindingOptions = new UnbindingOptions();
			options(unbindingOptions);
			var request = unbindingOptions.GetNewContext();
			return Unbind(request);
		}

		IDictionary<string, object> Unbind(IUnbindingRequest request)
		{
			new ISpecificUnbinder[] {_null, _simple, _enumerable, _complex}
				.First(x => x.AppropriatelyUnbinds(request))
				.Unbind(request);
			return request.Result;
		}
	}
}